// src/utils/websocket.ts
import ReceiveMessage from '/@/core/communication/ReceiveMessage';

type WsEventType = 'open' | 'message' | 'error' | 'close';

interface PairingMessage {
  type: 'Web' | 'Unity';
  message: string;
  pairKey: number;
}

interface WsEventMap {
  open: Event;
  message: MessageEvent<string>;
  error: Event;
  close: CloseEvent;
}

type MessageHandler<T = unknown> = (data: T) => void;
let isInitialized = false;

export class WebSocketClient {
  private ws: WebSocket | null = null;
  private pairKey = 1;
  private messageHandlers = new Map<string, MessageHandler>();
  private eventListeners = new Map<WsEventType, ((e: any) => void)[]>();
  private reconnectAttempts = 0;
  private maxReconnectAttempts = 3;
  private reconnectDelay = 1000;

  constructor() {}

  public init() {
    if (!isInitialized) {
      this.connect();
      isInitialized = true;
    }
  }

  public connect(url = `ws://${window.location.hostname}:8089`) {
    if (this.ws) {
      console.warn('WebSocket already initialized');
      return;
    }

    try {
      this.ws = new WebSocket(url);
    } catch (e) {
      return;
    }

    //connect消息
    this.ws.onopen = () => {
      this.startHeartbeat();
      this.on('message', (ev) => {
        try {
          const data = JSON.parse((<MessageEvent<string>>ev).data) as ReceiveUnityData;
          if (data.data === 'heartbeat') return;
          ReceiveMessage.executeEvent(data.type, data);
        } catch (e) {
          console.warn('Message parsing error:', e);
        }
      });
    };

    // 绑定基础事件监听器
    this.ws.addEventListener('open', this.handleOpen);
    this.ws.addEventListener('message', this.handleMessage);
    this.ws.addEventListener('error', this.handleError);
    this.ws.addEventListener('close', this.handleClose);
  }

  private handleOpen = (event: Event) => {
    this.emit('open', event);
    this.reconnectAttempts = 0;
  };

  private handleMessage = (event: MessageEvent<string>) => {
    try {
      if (event.data === 'heartbeat') return;
      const message = JSON.parse(event.data);

      // 优先处理系统消息
      if (message.type === 'pong') {
        return this.handlePong();
      }

      this.emit('message', event);

      // 触发特定消息类型处理器
      if (message.type && this.messageHandlers.has(message.type)) {
        this.messageHandlers.get(message.type)!(message);
      }
    } catch (error) {
      console.error('Message parsing error:', error);
    }
  };

  private handleError = (event: Event) => {
    console.error('WebSocket error:', event);
    this.emit('error', event);
  };

  private handleClose = (event: CloseEvent) => {
    console.log('WebSocket closed:', event.reason);
    this.emit('close', event);
    this.scheduleReconnect();
  };

  public sendPairingMessage(message: string) {
    const pairingMessage: PairingMessage = {
      type: 'Web',
      pairKey: this.pairKey,
      data: message,
    };
    this.send(pairingMessage);
  }

  private send<T extends object>(data: T) {
    if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
      console.error('WebSocket not connected');
      return;
    }

    try {
      this.ws.send(JSON.stringify(data));
    } catch (error) {
      console.error('Send message failed:', error);
    }
  }

  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  public on(event: WsEventType, handler: (payload: WsEventMap[typeof event]) => void) {
    const handlers = this.eventListeners.get(event) || [];
    handlers.push(handler);
    this.eventListeners.set(event, handlers);
  }

  public onMessage<T>(type: string, handler: MessageHandler<T>) {
    this.messageHandlers.set(type, handler as MessageHandler);
  }

  private emit<T extends WsEventType>(event: T, payload: WsEventMap[T]) {
    const handlers = this.eventListeners.get(event) || [];
    handlers.forEach((handler) => handler(payload));
  }

  private scheduleReconnect() {
    if (this.reconnectAttempts < this.maxReconnectAttempts) {
      setTimeout(() => {
        console.log(`Reconnecting (attempt ${this.reconnectAttempts + 1})`);
        this.connect();
        this.reconnectAttempts++;
      }, this.reconnectDelay * Math.pow(2, this.reconnectAttempts));
    }
  }

  public close() {
    if (this.ws) {
      this.ws.close(1000, 'Client closed');
      this.cleanup();
    }
  }

  private cleanup() {
    if (this.ws) {
      this.ws.removeEventListener('open', this.handleOpen);
      this.ws.removeEventListener('message', this.handleMessage);
      this.ws.removeEventListener('error', this.handleError);
      this.ws.removeEventListener('close', this.handleClose);
      this.ws = null;
    }
    this.messageHandlers.clear();
  }

  // 心跳检测
  private heartbeatInterval = 3000;
  private heartbeatTimer?: number;

  startHeartbeat() {
    this.heartbeatTimer = window.setInterval(() => {
      this.sendPairingMessage('heartbeat');
    }, this.heartbeatInterval);
  }

  // @ts-ignore
  private stopHeartbeat() {
    if (this.heartbeatTimer) {
      clearInterval(this.heartbeatTimer);
    }
  }

  private handlePong() {
    console.debug('Received pong');
  }
}

const wsClient = new WebSocketClient();
export default wsClient;

// 使用示例
export const useWebSocket = () => {
  // 自动重连配置
  // wsClient.on('close', (event) => {
  //   if (!event.wasClean) {
  //     console.log('Connection lost, attempting to reconnect...');
  //   }
  // });

  // 启动心跳检测
  wsClient.on('open', () => {
    wsClient.startHeartbeat();
  });

  return wsClient;
};
